-----------------------------------------------------------------------
-- Locals
--

local addon = CreateFrame("Frame")

local defaults = {
	profile = {
		posx = nil,
		posy = nil,
		lock = nil,
		width = 100,
		height = 80,
	}
}

local lineFormat = "%s: |cff%02x%02x%02x%d|r%%"
local currentPower = {}
local sortedPowerList = {}
local windowShown = nil

local unlock = "Interface\\AddOns\\Atramedes\\Textures\\icons\\lock"
local lock = "Interface\\AddOns\\Atramedes\\Textures\\icons\\un_lock"

local window = nil

local hexColors = {}
for k, v in pairs(RAID_CLASS_COLORS) do
	hexColors[k] = ("|cff%02x%02x%02x"):format(v.r * 255, v.g * 255, v.b * 255)
end
-- Helper table to cache colored player names.
local coloredNames = setmetatable({}, {__index =
	function(self, key)
		if type(key) == "nil" then return nil end
		local _, class = UnitClass(key)
		if class then
			self[key] = hexColors[class] .. key .. "|r"
			return self[key]
		else
			return key
		end
	end
})

local L = LibStub("AceLocale-3.0"):GetLocale("Atramedes")

-----------------------------------------------------------------------
-- Display Window
--

local function onDragStart(self) self:StartMoving() end
local function onDragStop(self)
	self:StopMovingOrSizing()
	local s = self:GetEffectiveScale()
	addon.db.profile.posx = self:GetLeft() * s
	addon.db.profile.posy = self:GetTop() * s
end
local function OnDragHandleMouseDown(self) self.frame:StartSizing("BOTTOMRIGHT") end
local function OnDragHandleMouseUp(self) self.frame:StopMovingOrSizing() end
local function onResize(self, width, height)
	addon.db.profile.width = width
	addon.db.profile.height = height
end

local locked = nil
local function lockDisplay()
	if locked then return end
	window:EnableMouse(false)
	window:SetMovable(false)
	window:SetResizable(false)
	window:RegisterForDrag()
	window:SetScript("OnSizeChanged", nil)
	window:SetScript("OnDragStart", nil)
	window:SetScript("OnDragStop", nil)
	window.drag:Hide()
	locked = true
end

local function unlockDisplay()
	if not locked then return end
	window:EnableMouse(true)
	window:SetMovable(true)
	window:SetResizable(true)
	window:RegisterForDrag("LeftButton")
	window:SetScript("OnSizeChanged", onResize)
	window:SetScript("OnDragStart", onDragStart)
	window:SetScript("OnDragStop", onDragStop)
	window.drag:Show()
	locked = nil
end

local function updateLockButton()
	if not window then return end
	window.lock:SetNormalTexture(addon.db.profile.lock and unlock or lock)
end

local function toggleLock()
	if addon.db.profile.lock then
		unlockDisplay()
	else
		lockDisplay()
	end
	addon.db.profile.lock = not addon.db.profile.lock
	updateLockButton()
end

local function onControlEnter(self)
	GameTooltip:ClearLines()
	GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
	GameTooltip:AddLine(self.tooltipHeader)
	GameTooltip:AddLine(self.tooltipText, 1, 1, 1, 1)
	GameTooltip:Show()
end
local function onControlLeave() GameTooltip:Hide() end
local function closeWindow() window:Hide() windowShown = false end

local function ensureDisplay()
	if window then return end

	local display = CreateFrame("Frame", "AtramedesAnchor", UIParent)
	display:SetWidth(addon.db.profile.width)
	display:SetHeight(addon.db.profile.height)
	display:SetMinResize(100, 30)
	display:SetClampedToScreen(true)
	local bg = display:CreateTexture(nil, "PARENT")
	bg:SetAllPoints(display)
	bg:SetBlendMode("BLEND")
	bg:SetTexture(0, 0, 0, 0.3)

	local close = CreateFrame("Button", nil, display)
	close:SetPoint("BOTTOMRIGHT", display, "TOPRIGHT", -2, 2)
	close:SetHeight(16)
	close:SetWidth(16)
	close.tooltipHeader = L["Close"]
	close.tooltipText = L["Closes the Atramedes loudness display."]
	close:SetNormalTexture("Interface\\AddOns\\Atramedes\\Textures\\icons\\close")
	close:SetScript("OnEnter", onControlEnter)
	close:SetScript("OnLeave", onControlLeave)
	close:SetScript("OnClick", closeWindow)

	local lock = CreateFrame("Button", nil, display)
	lock:SetPoint("BOTTOMLEFT", display, "TOPLEFT", 2, 2)
	lock:SetHeight(16)
	lock:SetWidth(16)
	lock.tooltipHeader = L["Toggle lock"]
	lock.tooltipText = L["Toggle whether or not the Atramedes window should be locked or not."]
	lock:SetScript("OnEnter", onControlEnter)
	lock:SetScript("OnLeave", onControlLeave)
	lock:SetScript("OnClick", toggleLock)
	display.lock = lock

	local header = display:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	header:SetText("Atramedes")
	header:SetPoint("BOTTOM", display, "TOP", 0, 4)

	local text = display:CreateFontString(nil, "OVERLAY", "GameFontNormalHuge")
	text:SetFont("Fonts\\FRIZQT__.TTF", 12)
	text:SetText("")
	text:SetAllPoints(display)
	display.text = text
	display:SetScript("OnShow", function() text:SetText("|cff777777:-P|r") end)

	local drag = CreateFrame("Frame", nil, display)
	drag.frame = display
	drag:SetFrameLevel(display:GetFrameLevel() + 10) -- place this above everything
	drag:SetWidth(16)
	drag:SetHeight(16)
	drag:SetPoint("BOTTOMRIGHT", display, -1, 1)
	drag:EnableMouse(true)
	drag:SetScript("OnMouseDown", OnDragHandleMouseDown)
	drag:SetScript("OnMouseUp", OnDragHandleMouseUp)
	drag:SetAlpha(0.5)
	display.drag = drag

	local tex = drag:CreateTexture(nil, "BACKGROUND")
	tex:SetTexture("Interface\\AddOns\\Atramedes\\Textures\\draghandle")
	tex:SetWidth(16)
	tex:SetHeight(16)
	tex:SetBlendMode("ADD")
	tex:SetPoint("CENTER", drag)

	window = display

	local x = addon.db.profile.posx
	local y = addon.db.profile.posy
	if x and y then
		local s = display:GetEffectiveScale()
		display:ClearAllPoints()
		display:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / s, y / s)
	else
		display:ClearAllPoints()
		display:SetPoint("CENTER", UIParent)
	end

	updateLockButton()
	if addon.db.profile.lock then
		locked = nil
		lockDisplay()
	else
		locked = true
		unlockDisplay()
	end
end

local function resetWindow()
	window:ClearAllPoints()
	window:SetPoint("CENTER", UIParent)
	window:SetWidth(defaults.profile.width)
	window:SetHeight(defaults.profile.height)
	addon.db.profile.posx = nil
	addon.db.profile.posy = nil
	addon.db.profile.width = nil
	addon.db.profile.height = nil
end

-----------------------------------------------------------------------
-- Utility
--

local function colorGradient(perc, ...)
	if perc >= 1 then
		local r, g, b = select(select("#", ...) - 2, ...)
		return r, g, b
	elseif perc <= 0 then
		local r, g, b = ...
		return r, g, b
	end
	local num = select("#", ...) / 3
	local segment, relperc = math.modf(perc*(num-1))
	local r1, g1, b1, r2, g2, b2 = select((segment*3)+1, ...)
	return r1 + (r2-r1)*relperc, g1 + (g2-g1)*relperc, b1 + (b2-b1)*relperc
end

local function getDisplayString(name)
	local r, g, b = colorGradient(currentPower[name] / 100, 0,255,0, 255,255,0, 255,0,0)
	return lineFormat:format(coloredNames[name], r, g, b, currentPower[name])
end

local tooLoud = {}
local function updateDisplay()
	wipe(tooLoud)
	for i = 1, #sortedPowerList do
		local n = sortedPowerList[i]
		if n and UnitExists(n) and not UnitIsDeadOrGhost(n) then
			tooLoud[#tooLoud + 1] = getDisplayString(n)
		end
		if #tooLoud > 4 or i > 25 then break end
	end
	if #tooLoud == 0 then
		window.text:SetText("|cff777777:-P|r")
	else
		window.text:SetText(table.concat(tooLoud, "\n"))
	end
end

local function sortFunc(a, b) return currentPower[a] > currentPower[b] end
local function updatePowers()
	wipe(sortedPowerList)
	for k in pairs(currentPower) do
		sortedPowerList[#sortedPowerList+1] = k
	end
	table.sort(sortedPowerList, sortFunc)
	updateDisplay()
end

local function checkForWipe()
	local w = true
	local num = GetNumRaidMembers()
	for i = 1, num do
		local name = GetRaidRosterInfo(i)
		if name then
			if UnitAffectingCombat(name) then
				w = false
			end
		end
	end
	if w and windowShown then
		wipe(sortedPowerList)
		updateDisplay()
	end
	if not w then addon:ScheduleTimer(checkForWipe, 2) end
end

-----------------------------------------------------------------------
-- Slash command
--

local function slashCommand(input)
	input = input:trim()
	if input == "reset" then
		resetWindow()
	elseif input == "lock" or input == "unlock" then
		toggleLock()
	else
		-- Make sure the window is there
		ensureDisplay()
		-- Start the show!
		window:Show()
		windowShown = true
		updateDisplay()
	end
end

-----------------------------------------------------------------------
-- Event handler
--

local function registerPowerEvents()
	addon:RegisterEvent("UNIT_POWER")
	addon:RegisterEvent("PLAYER_REGEN_ENABLED")
end

local function unregisterPowerEvents()
	addon:UnregisterEvent("UNIT_POWER")
	addon:UnregisterEvent("PLAYER_REGEN_ENABLED")
end

local function detectInstanceChange()
	if IsInInstance() then
		SetMapToCurrentZone()
	end
	local zone = GetRealZoneText()
	if zone == nil or zone == "" then
		-- zone hasn't been loaded yet, try again in 5 secs.
		addon:ScheduleTimer(detectInstanceChange, 5)
		return
	elseif GetCurrentMapAreaID() == 754 or GetCurrentMapAreaID() == 758 and IsInInstance() then
		registerPowerEvents()
	else
		unregisterPowerEvents()
	end
end

addon:RegisterEvent("ZONE_CHANGED_NEW_AREA")
addon:RegisterEvent("ADDON_LOADED")
addon:SetScript("OnEvent", function(self, event, unit, powerType)
	if event == "ADDON_LOADED" and unit == "Atramedes" then
		LibStub("AceTimer-3.0"):Embed(addon)
		self.db = LibStub("AceDB-3.0"):New("AtramedesDB", defaults, true)
		SlashCmdList.Atramedes = slashCommand
		SLASH_Atramedes1 = "/atramedes"
		detectInstanceChange()
		self:UnregisterEvent("ADDON_LOADED")
	elseif event == "ZONE_CHANGED_NEW_AREA" then
		detectInstanceChange()
	elseif event == "UNIT_POWER" and powerType == "ALTERNATE" and UnitInRaid(unit) and windowShown then
		local n = UnitName(unit)
		local power = UnitPower(unit, ALTERNATE_POWER_INDEX)
		if power < 0 then
			currentPower[n] = nil
		else
			currentPower[n] = power
		end
		updatePowers()
	elseif event == "PLAYER_REGEN_ENABLED" and windowShown then
		checkForWipe()
	end
end)

